/*
 *Zombie Defense Video Game Project
 *@Author: Jeremy Clark
 *@Tech: C++/SDL
*/

#ifdef __cplusplus
#include <cstdlib>
#else
#include <stdlib.h>
#endif
#ifdef __APPLE__
#include <SDL/SDL.h>
#else
#include <SDL/SDL.h>
#endif

#include <string>
#include <stdio.h>
#include "main.h"
#include "bullet.cpp"
#include "math.h"
#include "SDLGraphics.h"
#include "Input.h"
#include "Timer.h"
#include "player.cpp"
#include "gamemath.h"
#include "global.h"
#include "tower.cpp"
#include "collisionHandle.h"
#include "itemHandle.cpp"
//Constants
double const PI = 3.14159265;
const int framerate = 60;
const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const char* WINDOW_TITLE = "SDL Start";
const int BACKGROUND_RED = 255;
const int BACKGROUND_GREEN = 0;
const int BACKGROUND_BLUE = 255;
const int TRANS_R = 255;
const int TRANS_G = 0;
const int TRANS_B = 255;
//structs:
struct viewport
{
    int x,y,xmax,ymax,height,width;
};
//Global Variables
float angle;
char buffer [33];





SDL_Surface* cps;//CURRENT PLAYER SURFACE BASED ON ROTATION
viewport world;
viewport vport;
BulletPool p_bullets;
TowerPool p_towers;
itemHandle* g_items;
collisionHandle* g_col;
int mstate, mx, my;
player you;
bool g_gameIsRunning = true;
SDLGraphics* g_graphics = NULL;
Input* g_input = NULL;
Timer* g_timer = NULL;
float lasttime;
float frame;
float bullettimer;
char map[255][255];
int map_colsize = 0;
int map_rowsize = 0;
int laserdistance;
//sprites
SDL_Surface* i_playersurface[181];
SDL_Surface* i_turretsurface[181];
SDL_Surface* i_dirttileset[9];
SDL_Surface* i_man;
SDL_Surface* i_manguide;
SDL_Surface* i_tower;
SDL_Surface* i_towerguide;
SDL_Surface* i_turret;
SDL_Surface* i_turretguide;
SDL_Surface* i_tilegreen;
SDL_Surface* i_tilebrown;
SDL_Surface* i_tilered;
SDL_Surface* i_tilezombie;
SDL_Surface* i_itembuf;
SDL_Surface* i_items[9];
SDL_Surface* i_boxalpha;
SDL_Surface* i_toweralpha;
SDL_Surface* s_map;
//Prototypes
int quadtreetest(int start, int quadbuff);
float wtv_x(float x);
float wtv_y(float y);
float vtw_x(float x);
float vtw_y(float y);
void handleKeyboardInput();
void handleMouseInput();

int main ( int argc, char** argv )
{
	
    // initialize SDL video
    g_graphics = new SDLGraphics(WINDOW_WIDTH, WINDOW_HEIGHT,WINDOW_TITLE, BACKGROUND_RED, BACKGROUND_GREEN, BACKGROUND_BLUE);
    g_input = new Input();
	g_timer = new Timer();
	g_items = new itemHandle();
	g_col = new collisionHandle();
    freopen( "CON", "w", stdout );
    printf("Game started...");
    world.x = 0;
    world.y = 0;
    world.height = 1000;
    world.width = 1000;
    vport.x = 0;
    vport.y = 0;
	you.init();
    // make sure SDL cleans up before exit
    atexit(SDL_Quit);
    // load an image
	//player sprite, rotation & guide
    i_man = g_graphics->loadImage("rsc/man.png",TRANS_R,TRANS_G,TRANS_B);
	g_graphics->rotateSurface(i_man, 180,i_playersurface);
	g_graphics->closeBitmap(i_man);
	i_manguide = g_graphics->loadImage("rsc/man_guidelayer.png",TRANS_R,TRANS_G,TRANS_B);
	you.gunbottom =  gamemath::getSpriteGuideOffset(i_manguide,255,0,0);
	you.guntop = gamemath::getSpriteGuideOffset(i_manguide,0,255,0);
	//tower sprite & guide
	i_tower = g_graphics->loadImage("rsc/tower.png",TRANS_R,TRANS_G,TRANS_B);
	i_towerguide = g_graphics->loadImage("rsc/tower_guidelayer.png",TRANS_R,TRANS_G,TRANS_B);
	p_towers.towermiddle = gamemath::getSpriteGuideOffset(i_towerguide,255,0,0);
	//turret sprite, rotation & guide;
	i_turret = g_graphics->loadImage("rsc/turret.png",TRANS_R,TRANS_G,TRANS_B);
	g_graphics->rotateSurface(i_turret, 180,i_turretsurface);
	SDL_Surface* i_turretguide = g_graphics->loadImage("rsc/turret_guidelayer.png",TRANS_R,TRANS_G,TRANS_B);
	p_towers.gunmiddle =  gamemath::getSpriteGuideOffset(i_turretguide,255,0,0);
	p_towers.guntop = gamemath::getSpriteGuideOffset(i_turretguide,0,255,0);
	g_graphics->closeBitmap(i_towerguide);
	//background sprite
	i_tilegreen = g_graphics->loadImage("rsc/tile_green.png",TRANS_R,TRANS_G,TRANS_B);
	
	i_tilebrown = g_graphics->loadImage("rsc/dirt_ts.png",TRANS_R,TRANS_G,TRANS_B);
	g_graphics->seperateTileset(i_tilebrown, 9,32,32,i_dirttileset);
	g_graphics->closeBitmap(i_tilebrown);

	i_itembuf = g_graphics->loadImage("rsc/item_set.png",TRANS_R,TRANS_G,TRANS_B);
	g_graphics->seperateTileset(i_itembuf,9,32,32,i_items);
	g_graphics->closeBitmap(i_itembuf);

	i_tilered = g_graphics->loadImage("rsc/tile_red.png",TRANS_R,TRANS_G,TRANS_B);
	i_tilezombie = g_graphics->loadImage("rsc/z_spawn.png",TRANS_R,TRANS_G,TRANS_B);
	i_boxalpha = g_graphics->loadImage("rsc/box-alpha.png",TRANS_R,TRANS_G,TRANS_B);
	i_toweralpha = g_graphics->loadImage("rsc/tower-alpha.png",TRANS_R,TRANS_G,TRANS_B);
    // centre the bitmap on screen

	//load maps:
	int& map_ro = map_rowsize;//REFERENCES WOOO
	int& map_co = map_colsize;//REFERENCES :D
	gamemath::parsemap("at some point you should pass this", map,map_ro,map_co);
	cout << endl << map_colsize <<endl << map_rowsize << endl;
    SDL_Rect orig;
	world.height = map_colsize*32;
    world.width = map_rowsize*32;
    orig.x = 0;
    orig.y = 0;
    //run initilizations;
    
	s_map = g_graphics->createMap(map,map_colsize,map_rowsize);
		you.x = g_graphics->get_origin_x();
	you.y = g_graphics->get_origin_y();

	Uint32 LastTime = SDL_GetTicks();
	Uint32 ShownFrames = 0;
	float FPS = 0;
    while (g_gameIsRunning)
	{
        // Handle input
        g_input->readInput();
        if (g_input->windowClosed()){ g_gameIsRunning = false; }
		handleMouseInput();
        handleKeyboardInput();
		    //calculate the fps
		ShownFrames++;
 
		if((SDL_GetTicks() - LastTime) >= 1000)
		{
        FPS = ShownFrames;
 
        ShownFrames = 0;
        LastTime = SDL_GetTicks();
    }

        g_graphics->beginScene();
		g_graphics->drawSprite(s_map,wtv_x(0), wtv_y(0));
		
		if( ((you.x+(you.w/2))-(WINDOW_WIDTH/2) > 0) && ((you.x+(you.w/2))+(WINDOW_WIDTH/2) < world.width) ){
		vport.x = (you.x+(you.w/2))-(WINDOW_WIDTH/2);
		}
		if(((you.y+(you.h/2))-(WINDOW_HEIGHT/2) > 0) && (you.y+(you.h/2))+(WINDOW_HEIGHT/2) < world.height){
		vport.y = (you.y+(you.w/2))-(WINDOW_HEIGHT/2);
		}
		//handle game logic. Delay it so the game stays at a constant framerate. (needs work...)
		if ((g_timer->timeSinceCreation() - lasttime) >= 1.0f/60)
		{
			lasttime = g_timer->timeSinceCreation();
			p_bullets.Process();
			p_towers.Process();
			
			cps = i_playersurface[(int)abs((double)(angle/2))];
		}
        for (int i = 0; i < p_bullets.POOL_SIZE; i++)
        {
            if (p_bullets.bullets_[i].m_active)
            {
				g_graphics->drawLine(wtv_x(p_bullets.bullets_[i].x_),
									 wtv_y(p_bullets.bullets_[i].y_),
									 wtv_x(p_bullets.bullets_[i].x2_),
									 wtv_y(p_bullets.bullets_[i].y2_),
									 255,255,255,p_bullets.bullets_[i].alpha);
            }
        }
		
		for (int i = 0; i < p_towers.POOL_SIZE; i++)
        {
            if (p_towers.towers_[i].m_active)
            {
				g_graphics->addShadow(i_toweralpha,wtv_x(p_towers.towers_[i].x),wtv_y(p_towers.towers_[i].y));
			}
		}
		for (int i = 0; i < p_towers.POOL_SIZE; i++)
        {
            if (p_towers.towers_[i].m_active)
            {
				//g_graphics->addShadow(i_toweralpha,wtv_x(p_towers.towers_[i].x),wtv_y(p_towers.towers_[i].y));
				g_graphics->drawSprite(i_tower,wtv_x(p_towers.towers_[i].x),wtv_y(p_towers.towers_[i].y));
				g_graphics->drawSprite(i_turretsurface[(int)abs((double)(p_towers.towers_[i].angle/2))],
										wtv_x(p_towers.towers_[i].x+(i_tower->w/2))+((vport.width - i_turretsurface[(int)abs((double)(p_towers.towers_[i].angle/2))]->w)/2),
										wtv_y(p_towers.towers_[i].y+(i_tower->h/2))+((vport.height - i_turretsurface[(int)abs((double)(p_towers.towers_[i].angle/2))]->h)/2));
			}
        }
		for(int i = 0; i < g_items->MAX_ITEMS;i++)
		{
			if(g_items->items[i].active)
			{
				g_graphics->drawSprite(i_items[g_items->items[i].type],wtv_x(g_items->items[i].x),wtv_y(g_items->items[i].y));
			}
		}
		g_graphics->drawSprite(cps,wtv_x(you.x)+(you.w/2)+((vport.width - cps->w)/2),wtv_y(you.y)+(you.h/2)+((vport.height - cps->h)/2));//draw the player sprite, accounting for the stretching caused by the rotation.
		g_graphics->drawText("Undead Defense Commit#9",15,5,5,255,255,255,50,50,50);
		g_graphics->drawText(itoa(FPS,buffer,10),100,5,5,255,255,255,50,50,50);
		//being quadtree collision line thing:
		laserdistance = quadtreetest(0,128);
		g_graphics->drawLine(
				wtv_x(you.getguntopx(angle)),
				wtv_y(you.getguntopy(angle)),
				wtv_x(gamemath::angleoffsetx(you.getguntopx(angle),angle,laserdistance)),
				wtv_y(gamemath::angleoffsety(you.getguntopy(angle),angle,laserdistance)),
				255,15,35,155);
		g_graphics->endScene();
		frame++;
    } // end main loop

    //free memories
    g_graphics->closeBitmap(cps);
    delete g_timer;
    delete g_input;
    delete g_graphics;

    return 0;//ends the program
}

int quadtreetest(int start,int quadbuff)
{
	//g_graphics->drawRect(wtv_x(gamemath::angleoffsetx(you.getguntopx(angle),angle,start)),wtv_y(gamemath::angleoffsety(you.getguntopy(angle),angle,start)),wtv_x(gamemath::angleoffsetx(you.getguntopx(angle),angle,start+quadbuff)),wtv_y(gamemath::angleoffsety(you.getguntopy(angle),angle,start+quadbuff)),255,0,0,255);
		if(g_col->checkBox(gamemath::angleoffsetx(you.getguntopx(angle),angle,start),gamemath::angleoffsety(you.getguntopy(angle),angle,start),gamemath::angleoffsetx(you.getguntopx(angle),angle,start+quadbuff),gamemath::angleoffsety(you.getguntopy(angle),angle,start+quadbuff)))
		{
			
			if(quadbuff>1)
			{
				if(quadbuff<8)
				{
				quadtreetest(start, quadbuff-1);
				}
				else{
			quadtreetest(start, abs(quadbuff/2));
				}
			}
			else
			{
				return start;
			}
		}
		else
		{
		if(start<512){
			quadtreetest(start+quadbuff, quadbuff);
		}
		else
		{
			return 800;
		}
		}
		
		
}
void handleMouseInput()
{
	angle = gamemath::anglefromline(you.x+(you.w/2), you.y+(you.h/2),vtw_x(g_input->getMouseX()) ,vtw_y(g_input->getMouseY()));

    if(g_input->mouseDown == true)
    {
		if (g_input->mouseButton == SDL_BUTTON_LEFT)
		{
			if((g_timer->timeSinceCreation() -  bullettimer) >=0.1)
			{
				bullettimer = g_timer->timeSinceCreation();
				p_bullets.Create( you.getguntopx(angle), you.getguntopy(angle), angle, laserdistance);
			}
		}
	}
	if(g_input->mouseUp){
		if(g_input->mouseButton == SDL_BUTTON_RIGHT)
		{
			p_towers.Create(vtw_x(g_input->getMouseX()),vtw_y(g_input->getMouseY()));
			
		}
	}
}
void handleKeyboardInput()
{
    bool* keysHeld = g_input->getInput();
    if (keysHeld[SDLK_ESCAPE])
    {
        g_gameIsRunning = false;
    }

    if (keysHeld[SDLK_a])
    {
		if(!g_col->checkBox(you.collisionmodel.x-1,you.collisionmodel.y,you.collisionmodel.x-1+you.collisionmodel.h,you.collisionmodel.y+you.collisionmodel.w))
		{
        you.moveLeft(1);
		}
	}
    else if (keysHeld[SDLK_d])
    {
		if(!g_col->checkBox(you.collisionmodel.x+1,you.collisionmodel.y,you.collisionmodel.x+1+you.collisionmodel.h,you.collisionmodel.y+you.collisionmodel.w))
		{
        you.moveRight(1);
		}
    }
    if (keysHeld[SDLK_w])
    {
		if(!g_col->checkBox(you.collisionmodel.x,you.collisionmodel.y-1,you.collisionmodel.x+you.collisionmodel.h,you.collisionmodel.y-1+you.collisionmodel.w))
		{
        you.moveUp(1);
		}
    }
    else if (keysHeld[SDLK_s])
    {
		if(!g_col->checkBox(you.collisionmodel.x,you.collisionmodel.y+1,you.collisionmodel.x+you.collisionmodel.h,you.collisionmodel.y+1+you.collisionmodel.w))
		{
        you.moveDown(1);
		}
    }
    else
    {
        you.stopMoving();
    }
}

//the WTV functions convert "world" coordinates to "view" coordinates.
//the VTW function does the opposite.
float wtv_x(float x)
{
    return (x - vport.x);
}
float wtv_y(float y)
{
    return (y - vport.y);
}
float vtw_x(float x)
{
    return (x + vport.x);
}
float vtw_y(float y)
{
    return (y + vport.y);
}